/**
 *  @file StreamWriter.cpp
 */
#include "StreamWriter.h"
#include "../common/CppMemPool.h"
#include "../common/UtilFuncT.h"
#include "Stream.h"
#include "../collections/BinBuf.h"
#include "../text/CFString.h"
#include "__internal/WriteCharHelper.h"

using namespace cppflib::collections;

namespace cppflib
{

namespace io
{

StreamWriter::StreamWriter(void)
{
   this->pOrgStream = NULL;
   this->targetCharSet = text::CS_UTF8;
   this->pTempBuf = NULL;
   this->pTempArray = NULL;
}

/**
 *  @param [in] pStream -- output stream
 *  @param [in] targetCharSet -- target character set of the chars to write to the output stream
 */
StreamWriter::StreamWriter(Stream *pStream, text::charset_t targetCharSet)
{
   const sz_t BUFFERSIZE = 128;

   this->pOrgStream = pStream;
   cppmempool::Retain(this->pOrgStream);
   this->targetCharSet = targetCharSet;
   this->pTempBuf = new BinBuf(BUFFERSIZE);
   cppmempool::Retain(this->pTempBuf);
   this->pTempArray = new ArrayByte(BUFFERSIZE);
   cppmempool::Retain(this->pTempArray);
}

StreamWriter::~StreamWriter(void)
{
   Close();
}

void StreamWriter::Close()
{
   if (pOrgStream != NULL) {
      cppmempool::Release(pOrgStream);
      pOrgStream = NULL;
   }

   if (pTempArray != NULL) {
      cppmempool::Release(pTempArray);
      pTempArray = NULL;
   }

   if (pTempBuf != NULL) {
      cppmempool::Release(pTempBuf);
      pTempBuf = NULL;
   }
}

void StreamWriter::Flush()
{
   pOrgStream->Flush();
}

void StreamWriter::Write(ArrayWChar &buffer)
{
   Write(buffer, 0, static_cast<int>(buffer.GetSize()));
}

/**
 *  Write data to stream
 *
 *  @param [in] stream -- the target stream to write
 *  @param [in] srcBuf -- buffer containing source data
 *  @param [in] tempBuffer -- temp buffer for writing data to target stream
 */
PRIVATE void __WriteToStream(Stream &stream, BinBuf &srcBuf, ArrayByte &tempBuffer)
{
   if (srcBuf.GetValidLength() > 0) {
      u32_t srcBufIndex = 0;
      sz_t nrCopy = srcBuf.GetValidLength();
      while (nrCopy > 0) {
         sz_t copySize = cppflib::Min<sz_t>(nrCopy, tempBuffer.GetSize());
         // copy from srcBuf to tempBuffer
         tempBuffer.CopyFrom(srcBuf.GetRawBuf(), static_cast<u32_t>(srcBuf.GetValidLength()), 0, srcBufIndex, copySize);
         // write to stream
         stream.Write(tempBuffer, 0, static_cast<int>(copySize));

         nrCopy -= copySize;   // update
         srcBufIndex += copySize;
      }
   }
}

void StreamWriter::Write(ArrayWChar &buffer, int offset, int count)
{
   if (count < 0 || offset < 0 || offset >= static_cast<int>(buffer.GetSize()))
      return;

   // use the smaller one
   count = cppflib::Min<int>(count, static_cast<int>(buffer.GetSize() - offset));

   // write chars to binbuf first
   WriteCharsToBuffer(&buffer.GetRawPtr()[offset], count, *pTempBuf, targetCharSet);
   // write to stream finally
   __WriteToStream(*pOrgStream, *pTempBuf, *pTempArray);
}

void StreamWriter::Write(const CFString &str)
{
   Write(str, 0, str.GetLength());
}

void StreamWriter::Write(const CFString &str, int offset, int count)
{
   if (count < 0 || offset < 0 || offset >= str.GetLength())
      return;

   // use the smaller one
   count = cppflib::Min<int>(count, str.GetLength() - offset);

   // write chars to binbuf first
   WriteCharsToBuffer(&(static_cast<pcwstr_t>(str)[offset]), count, *pTempBuf, targetCharSet);
   // write to stream finally
   __WriteToStream(*pOrgStream, *pTempBuf, *pTempArray);
}

void StreamWriter::Write(int c)
{
   wc_t srcChar = static_cast<wc_t>(c);

   // write chars to binbuf first
   WriteCharsToBuffer(&srcChar, 1, *pTempBuf, targetCharSet);
   // write to stream finally
   __WriteToStream(*pOrgStream, *pTempBuf, *pTempArray);
}

} // end of namespace io

} // end of namespace cppflib
